#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/param.h>
#include <stdarg.h>

#include "main.h"
#include "bool.h"
#include "utils.h"
#include "xmalloc.h"
#include "template.h"
#include "reader.h"

const char tmpl_dir[MAXPATHLEN+1];
static tmpl_arr_t *templates = NULL;

int
set_template_dir(const char *dir)
{
  assert(dir != NULL);
  if (!isdir(dir) || access(dir, R_OK|X_OK)) {
    E("%s: template dir is not a valid directory or not readable", dir);
    return (1);
  }
  memset((char *)tmpl_dir, 0, MAXPATHLEN+1);
  strncpy((char *)tmpl_dir, dir, MAXPATHLEN);
  return (0);
}

static tmpl_data_t *
new_template_data(tmpl_data_t *last_elem)
{
  tmpl_data_t *tp;

  tp = (tmpl_data_t *) ncmalloc(1, sizeof(tmpl_data_t));
  tp->next = NULL;
  if (last_elem && !last_elem->next) {
    last_elem->next = tp;
  }
  return (tp);
}

tmpl_data_t *
read_template_files(const char *suffix)
{
  size_t idx = 0;
  dirlist_t *__tmpl_files;
  dirlist_elem_t *__tmpl_file;
  tmpl_data_t *tp, *tmp = NULL;

  D("parsing templates in: %s/*.%s", tmpl_dir, suffix);
  __tmpl_files = (dirlist_t *)listfiles((char *)tmpl_dir, suffix, NULL);
  if (!__tmpl_files) return (NULL);
  tp = tmp = new_template_data(NULL);
  while ((__tmpl_file = listfiles_foreach(__tmpl_files, &idx)) != NULL) {
    if (!mmap_data(__tmpl_file)) continue;
    tmp->path = create_dupstring(__tmpl_file->path);
    tmp->data = create_dupstring(__tmpl_file->data);
    tmp->next = NULL;
    tmp = new_template_data(tmp);
  }
  listfiles_cleanup(__tmpl_files);
  return (tp);
}

void
cleanup_template(tmpl_data_t *data)
{
  tmpl_data_t *tmp;

  if (!data) return;
  do {
    tmp = data->next;
    free_string(data->path);
    free_string(data->data);
    ncfree(data, sizeof(tmpl_data_t));
  } while ( (data = tmp) );
}

static void
register_tmpl(const tmpl_t *tp)
{
  array_add_data(templates, (void *)&tp, 1);
  if (tp->preinit_cb) {
    switch (tp->preinit_cb()) {
      case TMPLERR_OK:
        I("%s init ok", tp->name);
        break;
      case TMPLERR_NOFILES:
        E("%s init failed: no files", tp->name);
        break;
      default:
        E("%s init failed: unknown error", tp->name);
        break;
    }
  }
}

void
preinit_templates(void)
{
  templates = (tmpl_arr_t *) array_create(sizeof(tmpl_t *), 1);
  assert(templates);
  TEMPLATE_SYSTEMS
  assert(templates->used);
}

void
init_templates_thrd(void)
{
  size_t idx;
  tmpl_t *tmpl;
  if (!templates) return;
  for (idx = 0; idx < templates->used; idx++) {
    tmpl = *(tmpl_t **) array_get_offset(templates, idx);
    if (tmpl && tmpl->init_cb) tmpl->init_cb();
  }
}

void
free_templates(void)
{
  size_t idx;
  tmpl_t *tmpl;
  if (!templates) return;
  for (idx = 0; idx < templates->used; idx++) {
    tmpl = *(tmpl_t **) array_get_offset(templates, idx);
    if (tmpl && tmpl->free_cb) tmpl->free_cb();
  }
  array_free(templates);
}

void
templates_cleanup_thrd(void)
{
  size_t idx;
  tmpl_t *tmpl;
  if (!templates) return;
  for (idx = 0; idx < templates->used; idx++) {
    tmpl = *(tmpl_t **) array_get_offset(templates, idx);
    if (tmpl && tmpl->cleanup_cb) tmpl->cleanup_cb();
  }
}

int
tmpl_set_val(const tmpl_t *tmpl, const tmpl_data_t *tdat, const char *key, const char *value)
{
  if (tmpl->set_cb) {
    if ( tmpl->set_cb(tdat, key, value) == TMPLERR_OK ) return (TMPLERR_OK);
  }
  return (TMPLERR_FAILED);
}

int
tmpl_setf_val(const tmpl_t *tmpl, const tmpl_data_t *tdat, const char *key, const char *fmt, ...)
{
  int ret;
  char *buf = NULL;
  va_list ap;

  va_start(ap, fmt);
  vasprintf(&buf, fmt, ap);
  va_end(ap);
  ret = tmpl_set_val(tmpl, tdat, key, buf);
  free(buf);
  return (ret);
}

tmpl_data_t *
get_tmpl_ptr(const char *filename, const tmpl_t **tmpl_destptr)
{
  size_t idx;
  tmpl_t *tmpl;
  tmpl_data_t *dt;

  for (idx = 0; idx < templates->used; idx++) {
    tmpl = *(tmpl_t **) array_get_offset(templates, idx);
    if (tmpl->tmplptr_cb) {
      DD("template system %s: %s", tmpl->name, filename);
      if ((dt = tmpl->tmplptr_cb(filename)) != NULL) {
        DD("using template system %s: %s", tmpl->name, filename);
        if (tmpl_destptr) *tmpl_destptr = tmpl;
        return (dt);
      }
    }
  }
  return (NULL);
}

tmpl_data_t *
get_tmpl_ptr_from_attr(dict_t *dict, const char *default_tmpl, const tmpl_t **tmpl_destptr)
{
  const char *tmpl = lookup(dict, TMPL_ATTR);

  if (tmpl) {
    return (get_tmpl_ptr(tmpl, tmpl_destptr));
  } else {
    if (default_tmpl) {
      return (get_tmpl_ptr(default_tmpl, tmpl_destptr));
    } else {
      return (NULL);
    }
  }
}

string_t *
tmpl_parse_result(const tmpl_t *tmpl, const tmpl_data_t *tdat, string_t **str_ptr)
{
  string_t *result = NULL;

  if (tmpl->tmplresult_cb) {
    if ( (result = tmpl->tmplresult_cb(tdat, str_ptr)) ) return (result);
  }
  return (NULL);
}

void
tmpl_request(const tmpl_t *tmpl, const tmpl_data_t *tdat, const request_t *req)
{
  if (tmpl->tmplreq_cb) {
    tmpl->tmplreq_cb(tdat, req);
  }
}

